using System;
using FireStarterModeller.BO;
using FireStarterModeller.Logic;
using Habanero.Base;
using Habanero.BO;
using Habanero.BO.ClassDefinition;
using NUnit.Framework;

namespace FireStarterModeller.Test.Logic
{
    [TestFixture]
    public class TestDMClassEntityCreator
    {
        #region Setup/Teardown

        [TestFixtureSetUp]
        public virtual void TestFixtureSetup()
        {
            TestUtilsLogic.SetupFixture();
        }

        [SetUp]
        public virtual void SetupTest()
        {
            TestUtilsLogic.SetupTest();
        }

        [TearDown]
        public virtual void TearDownTest()
        {
        }

        #endregion

        [Test]
        public void Test_CreateEntity_ShouldCreateEntityWithCorrectClassDef()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsShared.CreateUnsavedValidDMClass();
            TestUtilsShared.CreateUnsavedValidDMProperty(dmClass);
            DMObjectIdentity objectIdentity = dmClass.CreateObjectIdentity(dmClass.Properties[0]);
            objectIdentity.IsObjectID = false;
            dmClass.ObjectIdentity = objectIdentity;
            ClassDefCol classDefCol = new DMToEntityClassDefsConverter().Convert((DMSolution) dmClass.Solution);
            IClassDef classDef = classDefCol["FireStarterModeller.BO", "Entity_" + dmClass.ClassNameBO];
            //---------------Assert Precondition----------------
            Assert.IsNotNull(classDef);
            //---------------Execute Test ----------------------
            Entity entity = DMClassEntityCreator.CreateEntity(dmClass, classDefCol);
            //---------------Test Result -----------------------
            Assert.IsNotNull(entity);
            Assert.AreEqual(dmClass.ClassNameBO, entity.ClassDef.TypeParameter);
            Assert.IsNotNull(entity.Props["EntityID"]);
            Assert.IsNotNull(entity.Props["DMClassID"]);
            Assert.AreEqual(dmClass.ClassID, entity.DMClassID);
        }

        [Test]
        public void Test_ConvertDMClass()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsShared.CreateUnsavedValidDMClass();
            TestUtilsShared.CreateUnsavedValidDMProperty(dmClass);
            DMObjectIdentity objectIdentity = dmClass.CreateObjectIdentity(dmClass.Properties[0]);
            objectIdentity.IsObjectID = false;
            dmClass.ObjectIdentity = objectIdentity;

            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            IClassDef classDef = new DMToEntityClassDefsConverter().Convert(dmClass);
            //---------------Test Result -----------------------
            Assert.IsNotNull(classDef);
            Assert.AreEqual("Entity_" + dmClass.ClassNameBO, classDef.ClassName);
            Assert.AreEqual(dmClass.ClassNameBO, classDef.TypeParameter);
        }

        [Test]
        public void Test_ConvertDMClass_WithNoObjectIdentity()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsShared.CreateUnsavedValidDMClass();

            //---------------Assert Precondition----------------
            Assert.AreEqual(0, dmClass.Properties.Count);
            Assert.IsNull(dmClass.ObjectIdentity);
            //---------------Execute Test ----------------------
            IClassDef classDef = new DMToEntityClassDefsConverter().Convert(dmClass);
            //---------------Test Result -----------------------
            Assert.AreEqual(0, dmClass.Properties.Count);
            Assert.IsNull(dmClass.ObjectIdentity);
            Assert.IsNotNull(classDef);
            Assert.AreEqual("Entity_" + dmClass.ClassNameBO, classDef.ClassName);
        }
        
        [Test]
        public void Test_ConvertDMSolution_AddsClassesToGlobalClassDefCol()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsShared.CreateUnsavedValidDMClass();

            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            ClassDefCol classDefCol = new DMToEntityClassDefsConverter().Convert((DMSolution) dmClass.Solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, classDefCol.Count);
            Assert.IsTrue(ClassDef.ClassDefs.Contains(classDefCol.FindByClassName("Entity_" + dmClass.ClassNameBO)));
        }
               
        [Test]
        public void Test_ConvertDMSolution_ReplacesClassesInGlobalClassDefCol()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsShared.CreateUnsavedValidDMClass();
            DMToEntityClassDefsConverter converter = new DMToEntityClassDefsConverter();
            converter.Convert((DMSolution) dmClass.Solution);
            //---------------Assert Precondition----------------
            //---------------Execute Test ----------------------
            ClassDefCol classDefCol = converter.Convert((DMSolution) dmClass.Solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(1, classDefCol.Count);
            Assert.IsTrue(ClassDef.ClassDefs.Contains(classDefCol.FindByClassName("Entity_" + dmClass.ClassNameBO)));
        }

        [Test]
        public void Test_ConvertDMSolution_WithSubclass()
        {
            //---------------Set up test pack-------------------
            DMClass superClass = TestUtilsShared.CreateUnsavedValidDMClass();
            superClass.ClassNameBO = "MyClass1";
            superClass.TypeParameter = "TypeParam1";
            DMClass subClass = TestUtilsShared.CreateUnsavedValidDMClass(superClass.Solution);
            subClass.ClassNameBO = "MyClass2";
            subClass.TypeParameter = "TypeParam2";
            subClass.InheritanceSuperClassRelationship = new DMInheritanceRelationship();
            subClass.InheritanceSuperClassRelationship.SuperClass = superClass;
            subClass.InheritanceSuperClassRelationship.InheritanceType = ORMapping.ClassTableInheritance;
            DMToEntityClassDefsConverter converter = new DMToEntityClassDefsConverter();
            //---------------Assert PreConditions---------------            
            //---------------Execute Test ----------------------
            ClassDefCol classDefCol = converter.Convert((DMSolution) superClass.Solution);
            //---------------Test Result -----------------------
            Assert.AreEqual(2, classDefCol.Count);
            Assert.IsTrue(ClassDef.ClassDefs.Contains(classDefCol.FindByClassName("Entity_" + superClass.ClassNameBO)));
            Assert.IsTrue(ClassDef.ClassDefs.Contains(classDefCol.FindByClassName("Entity_" + subClass.ClassNameBO)));
            //---------------Tear Down -------------------------          
        }
        
        [Test]
        public void Test_CreateEntity_WhenPrimaryKeyNotIsObjectID_ShouldReplaceWithEntityIDAndSetAsObjectID()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsLogic.CreateClassWithStringPropertyAsPK();
            TestUtilsLogic.AddPropertyWithNoRules(dmClass);
            TestUtilsLogic.AddPropertyWithNoRules(dmClass);

            dmClass.Assembly.Solution.Save();
            ClassDefCol classDefs = new DMToEntityClassDefsConverter().Convert((DMSolution) dmClass.Solution);

            //---------------Assert Preconditions---------------
            Assert.IsNotNull(dmClass.ObjectIdentity);
            Assert.IsFalse(dmClass.ObjectIdentity.IsObjectID.GetValueOrDefault());
            Assert.AreEqual("System.String", dmClass.ObjectIdentity.Properties[0].PropertyTypeString);
            //---------------Execute Test ----------------------
            Entity entity = DMClassEntityCreator.CreateEntity(dmClass, classDefs);
            //---------------Test Result -----------------------
            ClassDef entityClassDef = entity.ClassDef;
            const int standardEntityProps = 2;
            Assert.AreEqual
                (dmClass.Properties.Count + standardEntityProps, entityClassDef.PropDefcol.Count,
                 "The new ClassDef should have the three properties from the DMClass plus "
                 + "the two standard Props from the Entity (EntityID, DMClassID)");
            foreach (IBOProp prop in entity.Props)
            {
                Assert.IsTrue(entityClassDef.PropDefcol.Contains(prop.PropertyName));
            }
            foreach (DMProperty dmProp in dmClass.Properties)
            {
                Assert.IsTrue(entityClassDef.PropDefcol.Contains(dmProp.PropertyName));
            }
            Assert.IsTrue(entity.ClassDef.PrimaryKeyDef.IsGuidObjectID);
        }

        [Test]
        public void Test_SaveSecondEntity_ShouldNotFail()
        {
            //This test is testing that the Check for duplicates does not raise errors
            //when trying to compare the duplicate values in an in memory database.
            //---------------Set up test pack-------------------
            DMSolution solution = CreateSolutionWithTwoAssemblyOneClassEach();
            CreateClassDefsForDmSolution(solution);
            DMClass dmClass1 = solution.Assemblies[0].Classes[0];
            DMClass dmClass2 = solution.Assemblies[1].Classes[0];
            Entity entity1 = DMClassEntityCreator.CreateEntity(dmClass1, ClassDef.ClassDefs);
            entity1.Props[dmClass1.Properties[0].PropertyName].Value = Guid.NewGuid();
            entity1.Save();
            //---------------Assert Precondition----------------

            //---------------Execute Test ----------------------
            Entity entity2 = DMClassEntityCreator.CreateEntity(dmClass2, ClassDef.ClassDefs);
            entity2.Props[dmClass2.Properties[0].PropertyName].Value = Guid.NewGuid();
            entity2.Save();
            //---------------Test Result------------------------
            Assert.IsFalse(entity2.Status.IsNew, "This test is testing that the check for duplicates does not fail");
        }

        [Test]
        public void Test_CreateEntity_WhenHasDomainPrimaryKey_WithOneGuidProp_ShouldSetPropValueToEntityID()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsLogic.CreateClassWithOneGuidProperty();

            TestUtilsLogic.AddPropertyWithNoRules(dmClass);
            TestUtilsLogic.AddPropertyWithNoRules(dmClass);

            dmClass.Assembly.Solution.Save();
            ClassDefCol classDefs = new DMToEntityClassDefsConverter().Convert((DMSolution) dmClass.Solution);

            //---------------Assert Precondition----------------
            BusinessObjectCollection<DMProperty> properties = dmClass.ObjectIdentity.Properties;
            Assert.AreEqual(1, properties.Count);
            Assert.AreEqual("System.Guid", properties[0].PropertyTypeString);
            //---------------Execute Test ----------------------
            Entity entity = DMClassEntityCreator.CreateEntity(dmClass, classDefs);
            //---------------Test Result -----------------------
            Assert.IsNotNull(entity.EntityID);
            Assert.AreEqual(entity.EntityID, entity.GetPropertyValue(properties[0].PropertyName));
        }

        [Test]
        public void Test_CreateEntity_WhenHasDomainPrimaryKey_WithOneNonGuidProp_ShouldNotSetPropValueToEntityID()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsLogic.CreateClassWithStringPropertyAsPK();

            TestUtilsLogic.AddPropertyWithNoRules(dmClass);
            TestUtilsLogic.AddPropertyWithNoRules(dmClass);

            dmClass.Assembly.Solution.Save();
            ClassDefCol classDefs = new DMToEntityClassDefsConverter().Convert((DMSolution) dmClass.Solution);

            //---------------Assert Precondition----------------
            BusinessObjectCollection<DMProperty> properties = dmClass.ObjectIdentity.Properties;
            Assert.AreEqual(1, properties.Count);
            Assert.AreEqual("System.String", properties[0].PropertyTypeString);
            //---------------Execute Test ----------------------
            Entity entity = DMClassEntityCreator.CreateEntity(dmClass, classDefs);
            //---------------Test Result -----------------------
            Assert.IsNotNull(entity.EntityID);
            Assert.IsNull(entity.GetPropertyValue(properties[0].PropertyName));
        }

        [Test]
        public void Test_CreateEntity_WhenHasDomainPrimaryKey_With2PropsOneNonGuidProp_ShouldNotSetPropValueToEntityID()
        {
            //---------------Set up test pack-------------------
            DMClass dmClass = TestUtilsLogic.CreateClassWithOneGuidProperty();

            DMProperty dmProperty = TestUtilsLogic.AddGuidProperty(dmClass);
            dmClass.ObjectIdentity.Properties.Add(dmProperty);
            TestUtilsLogic.AddPropertyWithNoRules(dmClass);

            dmClass.Assembly.Solution.Save();
            ClassDefCol classDefs = new DMToEntityClassDefsConverter().Convert((DMSolution) dmClass.Solution);

            //---------------Assert Precondition----------------
            BusinessObjectCollection<DMProperty> objectIDProperties = dmClass.ObjectIdentity.Properties;
            Assert.AreEqual(2, objectIDProperties.Count);
            Assert.AreEqual("System.Guid", objectIDProperties[0].PropertyTypeString);
            Assert.AreEqual("System.Guid", objectIDProperties[1].PropertyTypeString);
            //---------------Execute Test ----------------------
            Entity entity = DMClassEntityCreator.CreateEntity(dmClass, classDefs);
            //---------------Test Result -----------------------
            Assert.IsNotNull(entity.EntityID);
            Assert.IsNull(entity.GetPropertyValue(objectIDProperties[0].PropertyName));
            Assert.IsNull(entity.GetPropertyValue(objectIDProperties[1].PropertyName));
        }

      

        private static DMSolution CreateSolutionWithTwoAssemblyOneClassEach()
        {
            SolutionCreator solutionCreator = new SolutionCreator();

            DMSolution solution = solutionCreator.CreateSolution();
            DMAssembly dmAssembly2 = solution.CreateAssembly(TestUtilsShared.GetRandomString());
            CreateClassWithOneGuidProp(solution.DefaultAssembly, TestUtilsShared.GetRandomString());
            CreateClassWithOneGuidProp(dmAssembly2, TestUtilsShared.GetRandomString());
            return solution;
        }

        private static void CreateClassWithOneGuidProp(DMAssembly dmAssembly, string className)
        {
            DMClass dmClass = dmAssembly.CreateClass(className);
            DMProperty prop = dmClass.CreateDMProp();
            prop.PropertyName = TestUtilsShared.GetRandomString();
            prop.PropertyTypeString = "System.Guid";
            dmClass.CreateObjectIdentity(prop);
        }

        private static void CreateClassDefsForDmSolution(DMSolution dmSolution)
        {
            DMToEntityClassDefsConverter converter = new DMToEntityClassDefsConverter();
            ClassDefCol defs = converter.Convert(dmSolution);
            foreach (ClassDef def in defs)
            {
                if (!ClassDef.ClassDefs.Contains(def))
                {
                    ClassDef.ClassDefs.Add(def);
                }
            }
        }
    }
}